package xj.util.gui;

import java.awt.geom.Rectangle2D;
import java.io.Serializable;

/**
 * Adapt from java.awt.geom.Rectangle2D.Float to make it serializable.
 * 
 * The <code>Rectangle2DFloat</code> class defines a rectangle specified in
 * float coordinates.
 * 
 * @since 1.2
 */
public class Rectangle2DFloat extends Rectangle2D implements Serializable {
  /**
   * The x coordinate of this <code>Rectangle2D</code>.
   * 
   * @since 1.2
   */
  public float x;

  /**
   * The y coordinate of this <code>Rectangle2D</code>.
   * 
   * @since 1.2
   */
  public float y;

  /**
   * The width of this <code>Rectangle2D</code>.
   * 
   * @since 1.2
   */
  public float width;

  /**
   * The height of this <code>Rectangle2D</code>.
   * 
   * @since 1.2
   */
  public float height;

  /**
   * Constructs a new <code>Rectangle2D</code>, initialized to location
   * (0.0,&nbsp;0.0) and size (0.0,&nbsp;0.0).
   * 
   * @since 1.2
   */
  public Rectangle2DFloat() {
  }

  /**
   * Constructs and initializes a <code>Rectangle2D</code> from the
   * specified float coordinates.
   * 
   * @param x,&nbsp;y
   *                the coordinates of the upper left corner of the newly
   *                constructed <code>Rectangle2D</code>
   * @param w
   *                the width of the newly constructed
   *                <code>Rectangle2D</code>
   * @param h
   *                the height of the newly constructed
   *                <code>Rectangle2D</code>
   * @since 1.2
         */
  public Rectangle2DFloat(float x, float y, float w, float h) {
    setRect(x, y, w, h);
  }

  /**
   * Returns the X coordinate of this <code>Rectangle2D</code> in double
   * precision.
   * 
   * @return the X coordinate of this <code>Rectangle2D</code>.
   * @since 1.2
   */
  @Override
  public double getX() {
    return x;
  }

  /**
   * Returns the Y coordinate of this <code>Rectangle2D</code> in double
   * precision.
   * 
   * @return the Y coordinate of this <code>Rectangle2D</code>.
   * @since 1.2
   */
  @Override
  public double getY() {
    return y;
  }

  /**
   * Returns the width of this <code>Rectangle2D</code> in double
   * precision.
   * 
   * @return the width of this <code>Rectangle2D</code>.
   * @since 1.2
   */
  @Override
  public double getWidth() {
    return width;
  }

  /**
   * Returns the height of this <code>Rectangle2D</code> in double
   * precision.
   * 
   * @return the height of this <code>Rectangle2D</code>.
   * @since 1.2
   */
  @Override
  public double getHeight() {
    return height;
  }

  /**
   * Determines whether or not this <code>Rectangle2D</code> is empty.
   * 
   * @return <code>true</code> if this <code>Rectangle2D</code> is
   *         empty; <code>false</code> otherwise.
   * @since 1.2
   */
  @Override
  public boolean isEmpty() {
    return (width <= 0.0f) || (height <= 0.0f);
  }

  /**
   * Sets the location and size of this <code>Rectangle2D</code> to the
   * specified float values.
   * 
   * @param x,&nbsp;y
   *                the coordinates to which to set the location of the
   *                upper left corner of this <code>Rectangle2D</code>
   * @param w
   *                the value to use to set the width of this
   *                <code>Rectangle2D</code>
   * @param h
   *                the value to use to set the height of this
   *                <code>Rectangle2D</code>
   * @since 1.2
   */
  public void setRect(float x, float y, float w, float h) {
    this.x = x;
    this.y = y;
    this.width = w;
    this.height = h;
  }

  /**
   * Sets the location and size of this <code>Rectangle2D</code> to the
   * specified double values.
   * 
   * @param x,&nbsp;y
   *                the coordinates to which to set the location of the
   *                upper left corner of this <code>Rectangle2D</code>
   * @param w
   *                the value to use to set the width of this
   *                <code>Rectangle2D</code>
   * @param h
   *                the value to use to set the height of this
   *                <code>Rectangle2D</code>
   * @since 1.2
   */
  @Override
  public void setRect(double x, double y, double w, double h) {
    this.x = (float) x;
    this.y = (float) y;
    this.width = (float) w;
    this.height = (float) h;
  }

  /**
   * Sets this <code>Rectangle2D</code> to be the same as the specified
   * <code>Rectangle2D</code>.
   * 
   * @param r
   *                the specified <code>Rectangle2D</code>
   * @since 1.2
   */
  @Override
  public void setRect(Rectangle2D r) {
    this.x = (float) r.getX();
    this.y = (float) r.getY();
    this.width = (float) r.getWidth();
    this.height = (float) r.getHeight();
  }

  /**
   * Determines where the specified float coordinates lie with respect to
   * this <code>Rectangle2D</code>. This method computes a binary OR of
   * the appropriate mask values indicating, for each side of this
   * <code>Rectangle2D</code>, whether or not the specified coordinates
   * are on the same side of the edge as the rest of this
   * <code>Rectangle2D</code>.
   * 
   * @param x,&nbsp;y
   *                the specified coordinates
   * @return the logical OR of all appropriate out codes.
   * @see Rectangle2D#OUT_LEFT
   * @see Rectangle2D#OUT_TOP
   * @see Rectangle2D#OUT_RIGHT
   * @see Rectangle2D#OUT_BOTTOM
   * @since 1.2
   */
  @Override
  public int outcode(double x, double y) {
    /*
     * Note on casts to double below. If the arithmetic of x+w or y+h is
     * done in float, then some bits may be lost if the binary exponents of
     * x/y and w/h are not similar. By converting to double before the
     * addition we force the addition to be carried out in double to avoid
     * rounding error in the comparison.
     * 
     * See bug 4320890 for problems that this inaccuracy causes.
     */
    int out = 0;
    if (this.width <= 0) {
      out |= OUT_LEFT | OUT_RIGHT;
    } else if (x < this.x) {
      out |= OUT_LEFT;
    } else if (x > this.x + (double) this.width) {
      out |= OUT_RIGHT;
    }
    if (this.height <= 0) {
      out |= OUT_TOP | OUT_BOTTOM;
    } else if (y < this.y) {
      out |= OUT_TOP;
    } else if (y > this.y + (double) this.height) {
      out |= OUT_BOTTOM;
    }
    return out;
  }

  /**
   * Returns the high precision bounding box of this
   * <code>Rectangle2D</code>.
   * 
   * @return the bounding box of this <code>Rectangle2D</code>.
   * @since 1.2
   */
  @Override
  public Rectangle2D getBounds2D() {
    return new Rectangle2DFloat(x, y, width, height);
  }

  /**
   * Returns a new <code>Rectangle2D</code> object representing the
   * intersection of this <code>Rectangle2D</code> with the specified
   * <code>Rectangle2D</code>.
   * 
   * @param r
   *                the <code>Rectangle2D</code> that is intersected
   *                with this <code>Rectangle2D</code>
   * @return the largest <code>Rectangle2D</code> contained in both the
   *         specified <code>Rectangle2D</code> and in this
   *         <code>Rectangle2D</code>.
   * @since 1.2
   */
  @Override
  public Rectangle2D createIntersection(Rectangle2D r) {
    Rectangle2D dest;
    if (r instanceof Rectangle2DFloat) {
      dest = new Rectangle2DFloat();
    } else if (r instanceof Rectangle2D.Float) {
      dest = new Rectangle2D.Float();
    } else {
      dest = new Rectangle2D.Double();
    }
    Rectangle2D.intersect(this, r, dest);
    return dest;
  }

  /**
   * Returns a new <code>Rectangle2D</code> object representing the
   * union of this <code>Rectangle2D</code> with the specified
   * <code>Rectangle2D</code>.
   * 
   * @param r
   *                the <code>Rectangle2D</code> to be combined with
   *                this <code>Rectangle2D</code>
   * @return the smallest <code>Rectangle2D</code> containing both the
   *         specified <code>Rectangle2D</code> and this
   *         <code>Rectangle2D</code>.
   * @since 1.2
   */
  @Override
  public Rectangle2D createUnion(Rectangle2D r) {
    Rectangle2D dest;
    if (r instanceof Rectangle2DFloat) {
      dest = new Rectangle2DFloat();
    } else if (r instanceof Rectangle2D.Float) {
      dest = new Rectangle2D.Float();
    } else {
      dest = new Rectangle2D.Double();
    }
    Rectangle2D.union(this, r, dest);
    return dest;
  }

  /**
         * Returns the <code>String</code> representation of this
         * <code>Rectangle2D</code>.
         * 
         * @return a <code>String</code> representing this
         *         <code>Rectangle2D</code>.
         * @since 1.2
         */
  @Override
  public String toString() {
    return getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + ",h="
	+ height + "]";
  }

}
